package il.ac.technion.wiki.api;

import java.util.SortedMap;

import il.ac.technion.wiki.api.IContentImporter.ContentException;
import il.ac.technion.wiki.api.IDataStore.DataStoreException;
import il.ac.technion.wiki.api.IDataStore.NotFoundException;
import il.ac.technion.wiki.impl.WikiEngine.WikiContent;

public interface IWikiEngine {
	/**
	 * Thrown to indicate an illegal query string.
	 *
	 */
	public static class BadQueryException extends Exception {
		private static final long serialVersionUID = -6686547953113187509L;
		public BadQueryException() { super(); }
		public BadQueryException(String message, Throwable cause) { super(message, cause); }
		public BadQueryException(String message) { super(message); }
		public BadQueryException(Throwable cause) { super(cause); }
	}

	/**
	 * Remove an importer for a given content-type from the wiki's internal list
	 * of content importers.
	 * @param contentType	The content type to remove import support for.
	 */
	public abstract void removeImporter(String contentType);

	/**
	 * Add an importer to the wiki's internal list of content importers. If
	 * the importer's handled content type was already handled by a different
	 * importer in the wiki's list, the old importer will be replaced by the one specified.
	 * @param importer	The importer to add.
	 */
	public abstract void addImporter(IContentImporter importer);
	
	/**
	 * Imports content into the wiki.
	 * @param content		The raw content.
	 * @param contentType	The content's mime-type.
	 * @param name TODO
	 * @return TODO
	 * @throws ContentException	If the import fails - either because the specified content
	 * 							type is unsupported or because the content's format is invalid
	 * 							for the given content type. 
	 */
	public abstract IWikiContent importContent(byte[] content, String contentType, String name) throws ContentException;

	/**
	 * Add a neutral word. (A word to ignore when performing searches.)
	 * @param word	The neutral word.
	 */
	public abstract void addNeutralWord(String word);

	/**
	 * Remove a neutral word. (A word to ignore when performing searches.)
	 * @param word	The neutral word.
	 */
	public abstract void removeNeutralWord(String word);

	/**
	 * Get a list of the currently specified neutral words.
	 * @return	An array of active neutral words.
	 */
	public abstract String[] getNeutralWords();

	/**
	 * Retrieves a piece of content with a specified name.
	 * @param name	The name of the content to retrieve.
	 * @return		The content object.
	 * @throws NotFoundException	If no content with the specified name could be found.
	 * @throws DataStoreException	If an error occurred accessing the data-store.
	 */
	public abstract IWikiContent get(String name) throws 	NotFoundException,
															DataStoreException;

	/**
	 * Searches the wiki for content matching the search expression.
	 * @param expr	A string search expression, in the format supported by the wiki.
	 * @return		A mapping of rank to objects matching the search query. The rank 
	 * 				is given by the content wiki's currently active ranker
	 * @throws DataStoreException	If an error occurred accessing the data-store.
	 * @throws BadQueryException	If the query string is illegal.
	 */
	public abstract SortedMap<Integer, WikiContent> search(String expr)
			throws DataStoreException, BadQueryException;

	/**
	 * Set the active content ranker for the wiki.
	 * @param ranker	The ranker that will be used from here on, until a
	 * 					different ranker is set.
	 */
	public abstract void setRanker(IContentRanker ranker);
	
	/**
	 * Represents a piece of wiki content.
	 * All modifications to the content are performed directly by invoking
	 * class methods. Note that <b>modifications will not take effect</b> until
	 * the {@link #save} method is invoked.
	 * Also note that any class implementing this interface should provide a
	 * default constructor; this is needed by data-store objects to be able
	 * to instantiate all types of content objects.
	 */
	public interface IWikiContent {
		public enum MetadataType {AUTHOR, LANGUAGE, DATE}
		
		/**
		 * Gets the name of the piece of content.
		 * @return	Content name.
		 */
		public String getName();
		
		/**
		 * Renames this piece of content.
		 * @param newName	The new name to set.
		 */
		public void setName(String newName);
		
		/**
		 * Returns the text related to this piece of content.
		 * @return	The text, in wiki-markup.
		 * 
		 */
		public String getText();
		
		/**
		 * Changes the text associated with this piece of content.
		 * @param text	The new piece of text to associate with the content.
		 */
		public void setText(String text);
		
		/**
		 * Gets the value associated with a piece of metadata for this content.
		 * @param key	The type of metadata to get.
		 * @return		The value of the metadata entry.
		 */
		public String getMetadata(MetadataType key);
		
		/**
		 * Sets the value associated with a piece of metadata for this content.
		 * @param key	The type of metadata to set.
		 * @param value	The string to associate with that metadata entry.
		 */
		public void setMetadata(MetadataType key, String value);
		
		/**
		 * Returns the content's current revision number.
		 * @return	The content's current revision number.
		 */
		public int getRevision();
		
		/**
		 * Explicitly sets the content's revision number.
		 * This is only meant for use by the data-store when re-constructing the object from
		 * storage - for example, by reading columns from an RDBMS.
		 * <b>Never use this directly or strange and distressing things will happens!</b>
		 * @param revision	The revision number to set. Must be positive!
		 */
		void setRevision(int revision);
		
		/**
		 * Will roll back the content to the specified revision.
		 * This operation will <b>cause the data-store to be hit immediately</b>
		 * to retrieve the historical information. However, you still need to invoke
		 * {@link #save} to permanently commit the change.
		 * @param revision	The revision to roll back to.
		 * @throws DataStoreException	If access to the data-store fails.
		 */
		public void rollBack(int revision) throws DataStoreException;
		
		/**
		 * <p>
		 * Will update the content to reflect any changes committed to the data-store.
		 * </p>
		 * <p>
		 * </i>Note: This will overwrite any changes that have been made since the object
		 * was last {@link #save saved}. Use {@link #isUpToDate} if you simply want to ensure
		 * that no changes have been made since you loaded the object.</i>
		 * </p>
		 * @throws DataStoreException	If access to the data-store fails.
		 */
		public void update() throws DataStoreException;
		
		/**
		 * Checks if the object in the data-store has been changed since this copy was
		 * loaded into memory.
		 * @return	<tt>false</tt> if changes have been found in the data-store, <tt>true</tt>
		 * otherwise.
		 * @throws DataStoreException
		 */
		public Boolean isUpToDate() throws DataStoreException;
		
		/**
		 * Commit changes to the data-store. Changes will not take effect
		 * until this method is invoked.
		 * Note that each invocation of this method will hit the data-store, as well
		 * as cause the revision to be incremented.
		 * @throws DataStoreException	If an error occurs while accessing the
		 * 								data-store. This can be any of the DataStoreException
		 * 								class hierarchy. Which ones to catch is determined by
		 * 								what changes have been performed. Note that DataStoreException
		 * 								may always be thrown due to unexpected errors accessing the
		 * 								underlying storage.
		 */
		public void save() throws DataStoreException;
		
		/**
		 * Marks all changes performed since the last save as applied. This will prevent
		 * the object from hitting the data-store for these changes the next time
		 * {@link #save} is invoked. This is useful mainly for the data-store when
		 * re-constructing an object read from storage. It may, however, cause 
		 * changes to be lost or other unexpected behavior if used incorrectly - so it is
		 * recommended direct calls to this method are avoided outside of data-store objects.
		 */
		public void apply();
		
		/**
		 * <p>
		 * Permanently delete this piece of content from the data-store, including
		 * all information and any history that might have been saved for it.
		 * </p>
		 * The object's internal state will be appropriately cleared to reflect this.<br>
		 * <i>This operation cannot be undone!</i>
		 * @throws DataStoreException	If access to the data-store fails.
		 */
		public abstract void delete() throws DataStoreException;
	}
	
	
}